Allow escaping of JSONPath outputs; demonstrate in the formatting agent.

Andrew Cantino 11 years ago
parent
commit
306237c306

+ 11 - 3
app/models/agents/event_formatting_agent.rb

@@ -24,7 +24,7 @@ module Agents
24 24
             subject: "$.data"
25 25
           }
26 26
 
27
-      JSONPaths must be between < and > . Make sure that you dont use these symbols anywhere else.
27
+      JSONPaths must be between < and > . Make sure that you don't use these symbols anywhere else.
28 28
 
29 29
       Events generated by this possible Event Formatting Agent will look like:
30 30
 
@@ -35,7 +35,13 @@ module Agents
35 35
 
36 36
       If you want to retain original contents of events and only add new keys, then set `mode` to `merge`, otherwise set it to `clean`.
37 37
 
38
-      By default, the output event will have `agent` and `created_at` fields as well, reflecting the original Agent type and Event creation time.  You can skip these outputs by setting `skip_agent` and `skip_created_at` to `true`.
38
+      By default, the output event will have `agent` and `created_at` fields added as well, reflecting the original Agent type and Event creation time.  You can skip these outputs by setting `skip_agent` and `skip_created_at` to `true`.
39
+
40
+      To CGI escape output (for example when creating a link), prefix with `escape`, like so:
41
+
42
+          {
43
+            :message => "A peak was on Twitter in <$.group_by>.  Search: https://twitter.com/search?q=<escape $.group_by>"
44
+          }
39 45
     MD
40 46
 
41 47
     event_description <<-MD
@@ -62,7 +68,9 @@ module Agents
62 68
     end
63 69
 
64 70
     def value_constructor(value, payload)
65
-      value.gsub(/<[^>]+>/).each {|jsonpath| Utils.values_at(payload,jsonpath[1..-2]).first.to_s }
71
+      value.gsub(/<[^>]+>/).each { |jsonpath|
72
+        Utils.values_at(payload, jsonpath[1..-2]).first.to_s
73
+      }
66 74
     end
67 75
 
68 76
     def receive(incoming_events)

+ 14 - 1
lib/utils.rb

@@ -1,4 +1,5 @@
1 1
 require 'jsonpath'
2
+require 'cgi'
2 3
 
3 4
 module Utils
4 5
   # Unindents if the indentation is 2 or more characters.
@@ -22,6 +23,18 @@ module Utils
22 23
   end
23 24
 
24 25
   def self.values_at(data, path)
25
-    JsonPath.new(path, :allow_eval => false).on(data.is_a?(String) ? data : data.to_json)
26
+    if path =~ /\Aescape /
27
+      path.gsub!(/\Aescape /, '')
28
+      escape = true
29
+    else
30
+      escape = false
31
+    end
32
+
33
+    result = JsonPath.new(path, :allow_eval => false).on(data.is_a?(String) ? data : data.to_json)
34
+    if escape
35
+      result.map {|r| CGI::escape r }
36
+    else
37
+      result
38
+    end
26 39
   end
27 40
 end

+ 4 - 0
spec/lib/utils_spec.rb

@@ -24,5 +24,9 @@ describe Utils do
24 24
       Utils.values_at({ :foo => [ { :bar => :baz }, { :bar => :bing } ]}, "foo[*].bar").should == %w[baz bing]
25 25
       Utils.values_at({ :foo => [ { :bar => :baz }, { :bar => :bing } ]}, "foo[*].bar").should == %w[baz bing]
26 26
     end
27
+
28
+    it "should allow escaping" do
29
+      Utils.values_at({ :foo => { :bar => "escape this!?" }}, "escape $.foo.bar").should == ["escape+this%21%3F"]
30
+    end
27 31
   end
28 32
 end

+ 117 - 108
spec/models/agents/event_formatting_agent_spec.rb

@@ -1,119 +1,128 @@
1 1
 require 'spec_helper'
2 2
 
3 3
 describe Agents::EventFormattingAgent do
4
-    before do
5
-        @valid_params = {
6
-            :name => "somename",
7
-            :options => {
8
-                :instructions => {
9
-                    :message => "Received <$.content.text.*> from <$.content.name> .",
10
-                    :subject => "Weather looks like <$.conditions>"
11
-                },
12
-                :mode => "clean",
13
-                :skip_agent => "false",
14
-                :skip_created_at => "false"
15
-            }
16
-        }
17
-        @checker = Agents::EventFormattingAgent.new(@valid_params)
18
-        @checker.user = users(:jane)
19
-        @checker.save!
20
-
21
-        @event = Event.new
22
-        @event.agent = agents(:jane_weather_agent)
23
-        @event.created_at = Time.now
24
-        @event.payload = {
25
-            :content => {
26
-                :text => "Some Lorem Ipsum",
27
-                :name => "somevalue"
4
+  before do
5
+    @valid_params = {
6
+        :name => "somename",
7
+        :options => {
8
+            :instructions => {
9
+                :message => "Received <$.content.text.*> from <$.content.name> .",
10
+                :subject => "Weather looks like <$.conditions>"
28 11
             },
29
-            :conditions => "someothervalue" 
12
+            :mode => "clean",
13
+            :skip_agent => "false",
14
+            :skip_created_at => "false"
30 15
         }
16
+    }
17
+    @checker = Agents::EventFormattingAgent.new(@valid_params)
18
+    @checker.user = users(:jane)
19
+    @checker.save!
20
+
21
+    @event = Event.new
22
+    @event.agent = agents(:jane_weather_agent)
23
+    @event.created_at = Time.now
24
+    @event.payload = {
25
+        :content => {
26
+            :text => "Some Lorem Ipsum",
27
+            :name => "somevalue"
28
+        },
29
+        :conditions => "someothervalue"
30
+    }
31
+  end
32
+
33
+  describe "#receive" do
34
+    it "should accept clean mode" do
35
+      @checker.receive([@event])
36
+      Event.last.payload[:content].should == nil
37
+    end
38
+
39
+    it "should accept merge mode" do
40
+      @checker.options[:mode] = "merge"
41
+      @checker.receive([@event])
42
+      Event.last.payload[:content].should_not == nil
43
+    end
44
+
45
+    it "should accept skip_agent" do
46
+      @checker.receive([@event])
47
+      Event.last.payload[:agent].should == "WeatherAgent"
48
+      @checker.options[:skip_agent] = "true"
49
+      @checker.receive([@event])
50
+      Event.last.payload[:agent].should == nil
51
+    end
52
+
53
+    it "should accept skip_created_at" do
54
+      @checker.receive([@event])
55
+      Event.last.payload[:created_at].should_not == nil
56
+      @checker.options[:skip_created_at] = "true"
57
+      @checker.receive([@event])
58
+      Event.last.payload[:created_at].should == nil
59
+    end
60
+
61
+    it "should handle JSONPaths in instructions" do
62
+      @checker.receive([@event])
63
+      Event.last.payload[:message].should == "Received Some Lorem Ipsum from somevalue ."
64
+      Event.last.payload[:subject].should == "Weather looks like someothervalue"
65
+    end
66
+
67
+    it "should allow escaping" do
68
+      @event.payload[:content][:name] = "escape this!?"
69
+      @event.save!
70
+      @checker.options[:instructions][:message] = "Escaped: <escape $.content.name>\nNot escaped: <$.content.name>"
71
+      @checker.save!
72
+      @checker.receive([@event])
73
+      Event.last.payload[:message].should == "Escaped: escape+this%21%3F\nNot escaped: escape this!?"
74
+    end
75
+
76
+    it "should handle multiple events" do
77
+      event1 = Event.new
78
+      event1.agent = agents(:bob_weather_agent)
79
+      event1.payload = {
80
+          :content => {
81
+              :text => "Some Lorem Ipsum",
82
+              :name => "somevalue"
83
+          },
84
+          :conditions => "someothervalue"
85
+      }
86
+
87
+      event2 = Event.new
88
+      event2.agent = agents(:bob_weather_agent)
89
+      event2.payload = {
90
+          :content => {
91
+              :text => "Some Lorem Ipsum",
92
+              :name => "somevalue"
93
+          },
94
+          :conditions => "someothervalue"
95
+      }
96
+
97
+      lambda {
98
+        @checker.receive([event2, event1])
99
+      }.should change { Event.count }.by(2)
100
+    end
101
+  end
102
+
103
+  describe "validation" do
104
+    before do
105
+      @checker.should be_valid
106
+    end
107
+
108
+    it "should validate presence of instructions" do
109
+      @checker.options[:instructions] = ""
110
+      @checker.should_not be_valid
111
+    end
112
+
113
+    it "should validate presence of mode" do
114
+      @checker.options[:mode] = ""
115
+      @checker.should_not be_valid
31 116
     end
32 117
 
33
-    describe "#receive" do
34
-        it "checks if clean mode is working fine" do
35
-            @checker.receive([@event])
36
-            Event.last.payload[:content].should == nil
37
-        end
38
-
39
-        it "checks if merge mode is working fine" do
40
-            @checker.options[:mode] = "merge"
41
-            @checker.receive([@event])
42
-            Event.last.payload[:content].should_not == nil
43
-        end
44
-
45
-        it "checks if skip_agent is working fine" do
46
-            @checker.receive([@event])
47
-            Event.last.payload[:agent].should == "WeatherAgent"
48
-            @checker.options[:skip_agent] = "true"
49
-            @checker.receive([@event])
50
-            Event.last.payload[:agent].should == nil
51
-        end
52
-
53
-        it "checks if skip_created_at is working fine" do
54
-            @checker.receive([@event])
55
-            Event.last.payload[:created_at].should_not == nil
56
-            @checker.options[:skip_created_at] = "true"
57
-            @checker.receive([@event])
58
-            Event.last.payload[:created_at].should == nil
59
-        end
60
-
61
-        it "checks if instructions are working fine" do
62
-            @checker.receive([@event])
63
-            Event.last.payload[:message].should == "Received Some Lorem Ipsum from somevalue ."
64
-            Event.last.payload[:subject].should == "Weather looks like someothervalue"
65
-        end
66
-
67
-        it "checks if it can handle multiple events" do
68
-            event1 = Event.new
69
-            event1.agent = agents(:bob_weather_agent)
70
-            event1.payload =  {
71
-            :content => {
72
-                :text => "Some Lorem Ipsum",
73
-                :name => "somevalue"
74
-                },
75
-            :conditions => "someothervalue" 
76
-            }
77
-
78
-            event2 = Event.new
79
-            event2.agent = agents(:bob_weather_agent)
80
-            event2.payload =  {
81
-            :content => {
82
-                :text => "Some Lorem Ipsum",
83
-                :name => "somevalue"
84
-                },
85
-            :conditions => "someothervalue" 
86
-            }
87
-
88
-        lambda {
89
-            @checker.receive([event2,event1])
90
-        }.should change { Event.count }.by(2)
91
-        end 
118
+    it "should validate presence of skip_agent" do
119
+      @checker.options[:skip_agent] = ""
120
+      @checker.should_not be_valid
92 121
     end
93 122
 
94
-    describe "validation" do
95
-        before do
96
-            @checker.should be_valid
97
-        end
98
-
99
-        it "should validate presence of instructions" do
100
-            @checker.options[:instructions] = ""
101
-            @checker.should_not be_valid
102
-        end
103
-
104
-        it "should validate presence of mode" do
105
-            @checker.options[:mode] = ""
106
-            @checker.should_not be_valid
107
-        end
108
-
109
-        it "should validate presence of skip_agent" do
110
-            @checker.options[:skip_agent] = ""
111
-            @checker.should_not be_valid
112
-        end
113
-
114
-        it "should validate presence of skip_created_at" do
115
-            @checker.options[:skip_created_at] = ""
116
-            @checker.should_not be_valid
117
-        end
123
+    it "should validate presence of skip_created_at" do
124
+      @checker.options[:skip_created_at] = ""
125
+      @checker.should_not be_valid
118 126
     end
127
+  end
119 128
 end